package com.mjk.common.core.redis.datatype;

import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 *
 * LPUSH：将一个或多个值插入到列表的头部。
 * RPUSH：将一个或多个值插入到列表的尾部。
 * LPOP：移除并返回列表的头部元素。
 * RPOP：移除并返回列表的尾部元素。
 * LLEN：返回列表的长度。
 * LRANGE：返回列表中指定范围内的元素。
 * LINDEX：返回列表中指定索引位置的元素。
 * LSET：设置列表中指定索引位置的元素值。
 *
 */
public class RedisListUtils {
	private RedisListUtils(){}
	/**
	 * * 将一个值插入到列表头部，value可以重复，返回列表的长度 @param key
	 * 
	 * @param value String @return 返回List的长度
	 */
	public static Long lpush(RedisTemplate<String, Object> redisTemplate,String key, String value) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.leftPush(key, value);
	}

	/**
	 * * 将多个值插入到列表头部，value可以重复 * @param key * @param values String[] * @return
	 * 返回List的数量size
	 */
	public static Long lpush(RedisTemplate<String, Object> redisTemplate,String key, Object[] values) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.leftPushAll(key, values);		
	}

	/**
	 * * 获取List列表 * @param key * @param start long，开始索引 * @param end long， 结束索引
	 * * @return List<String>
	 */
	public static List<Object> lrange(RedisTemplate<String, Object> redisTemplate,String key, long start, long end) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.range(key, start, end);
	}

	/**
	 * * 通过索引获取列表中的元素 * @param key * @param index，索引，0表示最新的一个元素 * @return String
	 */
	public static Object lindex(RedisTemplate<String, Object> redisTemplate,String key, long index) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.index(key, index);
	}

	/**
	 * * 获取列表长度，key为空时返回0 * @param key * @return Long
	 */
	public static Long llen(RedisTemplate<String, Object> redisTemplate,String key) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.size(key);
	}

	/**
	 * * 在列表的元素前或者后插入元素，返回List的长度 * @param key * @param where LIST_POSITION * @param
	 * pivot 以该元素作为参照物，是在它之前，还是之后（pivot：枢轴;中心点，中枢;[物]支点，支枢;[体]回转运动。） * @param value
	 * * @return Long
	 */
	public static Long linsert(RedisTemplate<String, Object> redisTemplate,String key, Position where, String pivot, String value) {
		RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
		if(null != factory){
			RedisConnection connection = factory.getConnection();
			return connection.lInsert(key.getBytes(), where, pivot.getBytes(), value.getBytes());
		}
		return -1L;
	}



	/**
	 * 移除列表元素，返回移除的元素数量
	 * 
	 * @param key
	 * @param       count，标识，表示动作或者查找方向
	 *              <li>当count=0时，移除所有匹配的元素；</li>
	 *              <li>当count为负数时，移除方向是从尾到头；</li>
	 *              <li>当count为正数时，移除方向是从头到尾；</li>
	 * @param value 匹配的元素
	 * @return Long
	 */
	public static Long lrem(RedisTemplate<String, Object> redisTemplate,String key, long count, String value) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.remove(key, count, value);		
	}

	/**
	 * 通过索引设置列表元素的值，当超出索引时会抛错。成功设置返回true
	 * 
	 * @param key
	 * @param index 索引
	 * @param value
	 * @return boolean
	 */
	public static void lset(RedisTemplate<String, Object> redisTemplate,String key, long index, String value) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		listOperations.set(key, index, value);
				
	}

	/**
	 * 对一个列表进行修剪(trim)，就是说，让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除。
	 * 
	 * @param key
	 * @param start
	 *              <li>可以为负数（-1是列表的最后一个元素，-2是列表倒数第二的元素。）</li>
	 *              <li>如果start大于end，则返回一个空的列表，即列表被清空</li>
	 * @param end
	 *              <li>可以为负数（-1是列表的最后一个元素，-2是列表倒数第二的元素。）</li>
	 *              <li>可以超出索引，不影响结果</li>
	 * @return boolean
	 */
	public static void ltrim(RedisTemplate<String, Object> redisTemplate,String key, long start, long end) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		listOperations.trim(key, start, end);
	}

	/**
	 * 移出并获取列表的第一个元素，当列表不存在或者为空时，返回Null
	 * 
	 * @param key
	 * @return String
	 */
	public static Object lpop(RedisTemplate<String, Object> redisTemplate,String key) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.leftPop(key);
	}

	/**
	 * 移除并获取列表最后一个元素，当列表不存在或者为空时，返回Null
	 * 
	 * @param key
	 * @return String
	 */
	public static Object rpop(RedisTemplate<String, Object> redisTemplate,String key) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.rightPop(key);
	}

	/**
	 * 在列表中的尾部添加一个个值，返回列表的长度
	 * 
	 * @param key
	 * @param value
	 * @return Long
	 */
	public static Long rpush(RedisTemplate<String, Object> redisTemplate,String key, String value) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.rightPush(key, value);		
	}

	/**
	 * 在列表中的尾部添加多个值，返回列表的长度
	 * 
	 * @param key
	 * @param values
	 * @return Long
	 */
	public static Long rpush(RedisTemplate<String, Object> redisTemplate,String key, String[] values) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.rightPush(key, values);	
	}

	

	/**
	 * 移除列表的最后一个元素，并将该元素添加到另一个列表并返回
	 * 
	 * @param sourceKey 源列表的key，当源key不存在时，结果返回Null
	 * @param targetKey 目标列表的key，当目标key不存在时，会自动创建新的
	 * @return String
	 */
	public static Object rpopLpush(RedisTemplate<String, Object> redisTemplate,String sourceKey, String targetKey) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.rightPopAndLeftPush(sourceKey, targetKey);				
	}

	/**
	 * 移出并获取列表的【第一个元素】， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
	 * 
	 * @param timeout 单位为秒
	 * @param keys
	 *                <li>当有多个key时，只要某个key值的列表有内容，即马上返回，不再阻塞。</li>
	 *                <li>当所有key都没有内容或不存在时，则会阻塞，直到有值返回或者超时。</li>
	 *                <li>当超期时间到达时，keys列表仍然没有内容，则返回Null</li>
	 * @return List<String>
	 */
	public static List<Object> blpop(RedisTemplate<String, Object> redisTemplate,int timeout, String... keys) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		List<Object> values = new ArrayList<>();
		for(String str : keys) {
			values.add(listOperations.leftPop(str, timeout, TimeUnit.SECONDS));
		}
		return values;
	}

	/**
	 * 移出并获取列表的【最后一个元素】， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
	 * 
	 * @param timeout 单位为秒
	 * @param keys
	 *                <li>当有多个key时，只要某个key值的列表有内容，即马上返回，不再阻塞。</li>
	 *                <li>当所有key都没有内容或不存在时，则会阻塞，直到有值返回或者超时。</li>
	 *                <li>当超期时间到达时，keys列表仍然没有内容，则返回Null</li>
	 * @return List<String>
	 */
	public static List<Object> brpop(RedisTemplate<String, Object> redisTemplate,int timeout, String... keys) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		List<Object> values = new ArrayList<>();
		for(String str : keys) {
			values.add(listOperations.rightPop(str, timeout, TimeUnit.SECONDS));
		}
		return values;
	}

	/**
	 * 从列表中弹出列表最后一个值，将弹出的元素插入到另外一个列表中并返回它； 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
	 * 
	 * @param sourceKey 源列表的key，当源key不存在时，则会进行阻塞
	 * @param targetKey 目标列表的key，当目标key不存在时，会自动创建新的
	 * @param timeout   单位为秒
	 * @return String
	 */
	public static Object brpopLpush(RedisTemplate<String, Object> redisTemplate,String sourceKey, String targetKey, int timeout) {
		ListOperations<String, Object> listOperations = redisTemplate.opsForList();
		return listOperations.rightPopAndLeftPush(sourceKey, targetKey, timeout, TimeUnit.SECONDS);
	}
	/**
	 * 缓存List数据
	 * 
	 * @param key
	 *            缓存的键值
	 * @param dataList
	 *            待缓存的List数据
	 * @return 缓存的对象
	 */
	public static ListOperations<String, Object> setCacheList(RedisTemplate<String, Object> redisTemplate, 
			String key,List<Object> dataList) {
		ListOperations<String, Object> listOperation = redisTemplate.opsForList();
		if (null != dataList) {
			int size = dataList.size();
			for (int i = 0; i < size; i++) {
				if (dataList.get(i) != null) {
					listOperation.rightPush(key, dataList.get(i));
				}
			}
		}

		return listOperation;
	}

	/**
	 * 清除list
	 * 
	 * @param redisTemplate
	 * @param key
	 * @return
	 */
	public static long clearCacheList(RedisTemplate<?, ?> redisTemplate,final String key) {
		Long value =redisTemplate.execute((RedisConnection connection)->{
			long result = 0;
			result = connection.del(key.getBytes());
			return result;
		});
		if(null == value){
			return -1;
		}
		return value;
	}

	/**
	 * 获得缓存的list对象
	 * 
	 * @param key
	 *            缓存的键值
	 * @return 缓存键值对应的数据
	 */
	public static List<Object> getCacheList(RedisTemplate<String, Object> redisTemplate, String key) {
		List<Object> dataList = new ArrayList<>();
		ListOperations<String, Object> listOperation = redisTemplate.opsForList();
		Long size = listOperation.size(key);
		if(null == size){
			size = 0L;
		}
		for (int i = 0; i < size; i++) {
			dataList.add(listOperation.leftPop(key));
		}
		return dataList;
	}
}